Keep unwrap() key order after a value is replaced - #591
Open
youdie006 wants to merge 2 commits into
Open
Conversation
_replace_at() removes the key from _map and re-inserts it, which moves it
to the end of the dict's insertion order while _body keeps the original
slot. dumps() and keys() walk _body and stay correct; unwrap() walks _map
and does not:
doc = parse("a = 1\nb = 2\nc = 3\n")
doc["b"] = 9
dumps(doc) # 'a = 1\nb = 9\nc = 3\n'
list(doc.keys()) # ['a', 'b', 'c']
list(doc.unwrap()) # ['a', 'c', 'b']
unwrap() moved to _map in python-poetry#521 on the stated assumption that it "iterates
in the same insertion order as the old self.items()", which a replacement
breaks. Order comes from the body index instead, so the _map fast path is
kept and dumps() stays the reference.
Replacements that genuinely move an element still move: a bare key
promoted to a table has to be emitted after the inline entries, and
unwrap() follows the body there too. Both cases are covered.
for more information, see https://pre-commit.ci
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
unwrap()returns keys in the wrong order after a value is replaced._replace_atdeletes the key from_mapand re-inserts it (container.py:916-917), which moves it to the end of the dict's insertion order, while_body[idx]correctly keeps the original slot.dumps()andkeys()walk_bodyand stay right;unwrap()walks_mapand does not. It reproduces for every scalar and array replacement value.This is against the README's own promise:
and against the assumption
unwrap()was written on when it moved to_mapin #521:That sentence is true until the first replacement, which is why it held when #521 landed.
The change
Take the order from the body index rather than from
_mapinsertion order. The_mapfast path that #521 added is kept — the point of that change was avoiding__getitem__rebuilding aSingleKeyper key, and that still holds — anddumps()becomes the reference for order rather than a second, divergent source.I looked at the other
_map.items()loops before choosing this over rewriting_replace_at:container.py:542and:583only adjust index values and do not care about order, sounwrap()is the sole order-sensitive consumer.Not changed: replacements that genuinely move an element
A bare key promoted to a table has to be emitted after the inline entries, and
dumps()moves it:Ordering by the body index keeps this case as it was, since the body really did move. There is a test pinning it so the distinction cannot regress.
Tests
Two added to
tests/test_toml_document.py: one asserting the order is preserved on a scalar replacement, one pinning the table-promotion case above.Verified by reverting only
tomlkit/container.pyand keeping the tests — the first fails withassert ['a', 'c', 'b'] == ['a', 'b', 'c'], the second still passes, so the new coverage separates the bug from the behaviour that should not change.Full suite: 1052 passing before, 1054 after.
Agent Drafting Metadata